using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using Av.Utils;
using System.Net;
using Solo.Core;
using System.IO;

namespace SiteStatusTracker
{

    /// <summary>
    /// Delegate for operation completed
    /// </summary>
    delegate void SiteIsTrackedDelegate(TrackerItem item);

    /// <summary>
    /// Delegate for whole list of sites is checked
    /// </summary>
    /// <param name="arSite"></param>
    delegate void SiteWholeListDoneDelegate(TrackerItem[] arSite);

    class GetStatusThread
    {
        /// <summary>
        /// Fired when site successfully checked
        /// </summary>
        public event SiteIsTrackedDelegate SiteTrackedOk = null;

        /// <summary>
        /// Calls when site check fails
        /// </summary>
        public event SiteIsTrackedDelegate SiteTrackedFail = null;

        /// <summary>
        /// Fires when whole list is checked
        /// </summary>
        public event SiteWholeListDoneDelegate AllSitesDone = null;

        /// <summary>
        /// Do we need to track site status
        /// </summary>
        protected bool m_isWorking = false;

        protected Thread m_thread = null;

        /// <summary>
        /// Contains web client to track sites
        /// </summary>
        protected WebClient m_webClient = null;

        //protected TrackerItem[] m_sitesToTrack = null;

        public GetStatusThread()
        {
            if( Settings.TrackerItems != null )
            {
                //m_sitesToTrack = new TrackerItem[1];
                //m_sitesToTrack[0] = Settings.TrackerItems[0];
                //m_sitesToTrack = Settings.TrackerItems;

                m_webClient = new WebClient();
                //m_webClient.OpenReadCompleted += new OpenReadCompletedEventHandler(OnOpenReadCompleted);
                m_webClient.DownloadStringCompleted += new DownloadStringCompletedEventHandler(OnDownloadStringCompleted);
            } else
            {
                Log4cs.Log(Importance.Error, "No task for status trhead!");
                throw new Exception("Task configuration exception!");
            }
        }

        /// <summary>
        /// Searches for specified text on completion
        /// </summary>
        void OnDownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
        {
            try
            {
                TrackerItem site = new TrackerItem();
                bool isFound = false;

                site = (TrackerItem)e.UserState;
                Log4cs.Log("Site {0} length is: {1}b", site.Name, e.Result.Length);

                if( !string.IsNullOrEmpty(e.Result) && !string.IsNullOrEmpty(site.TextInPage) )
                {
                    isFound = e.Result.Contains(site.TextInPage);
                    Log4cs.Log("Specific site text was {0}found...", isFound ? "" : "NOT ");
                }

                // If text is found and we need to handle it
                if( isFound && (SiteTrackedOk != null) )
                {
                    SiteTrackedOk(site);
                }

                // If text is not found and we need to handle it
                if( !isFound && (SiteTrackedFail != null) )
                {
                    SiteTrackedFail(site);
                }

            } catch( Exception ex )
            {
                Log4cs.Log(Importance.Error, "Error OnDownloadStringCompleted!");
                Log4cs.Log(Importance.Debug, ex.ToString());
                
            }
        }

        void OnOpenReadCompleted(object sender, OpenReadCompletedEventArgs e)
        {
            TrackerItem site = new TrackerItem();
            bool isOk = false;
            try
            {
                site = (TrackerItem)e.UserState;
                byte[] ba = new byte[1024];
                string sPage = "";

                Log4cs.Log("{0,-32} opened...", site.SiteUri);
                site.ReceivedBytes = e.Result.Read(ba, 0, ba.Length);
                Log4cs.Log("{0,-32} read: {1} bytes", site.SiteUri, site.ReceivedBytes);
                if( site.ReceivedBytes == ba.Length )
                {
                    sPage = Encoding.UTF8.GetString(ba);
                    //Log4cs.Log(Importance.Debug, "{0}{1}{2}", site.SiteUri, Environment.NewLine, Encoding.UTF8.GetString(ba));
                    if( !string.IsNullOrEmpty(sPage) && !string.IsNullOrEmpty(site.TextInPage) )
                    {
                        isOk = sPage.Contains(site.TextInPage);
                        Log4cs.Log("Specific site text was {0}found...", isOk ? "" : "NOT ");
                    }
                }
                e.Result.Close();
            } catch( Exception ex )
            {
                Log4cs.Log(Importance.Error, "Error on OpenReadCompelete!");
                Log4cs.Log(Importance.Debug, ex.ToString());
            } finally
            {
                if( isOk && (SiteTrackedOk != null) )
                {
                    Log4cs.Log(Importance.Debug, "Calling SiteTrackedOk callback...");
                    SiteTrackedOk(site);
                }

                if( !isOk && (SiteTrackedFail != null) )
                {
                    Log4cs.Log(Importance.Debug, "Calling SiteTrackedFail callback...");
                    SiteTrackedFail(site);
                }
            }
        }

        /// <summary>
        /// Gets/sets is thread is tracking sites
        /// </summary>
        public bool Working
        {
            get { return m_isWorking; }
            set
            {
                lock( this )
                {
                    m_isWorking = value;
                }
            }
        }

        public void Start()
        {
            if( m_thread != null )
            {
                throw new Exception("Thread is already working!");
            }

            Working = true;
            m_thread = new Thread(new ParameterizedThreadStart(TrackerThread));
            m_thread.Start(null);
        }


        protected void TrackerThread(object obj)
        {
            Log4cs.Log("Tacker thread starts...");

            try
            {
                int[] arEventIds = { Settings.Events.EventId.Exit };
                int eventId = ManualResetEvent.WaitTimeout;

                while( Working )
                {
                    // Opens stream to site. Should we use DownloadString?!
                    //m_webClient.OpenReadAsync(m_sitesToTrack[0].SiteUri, m_sitesToTrack[0]);
                    //for( int i = 0; i < m_sitesToTrack.Length; i++ )
                    for( int i = 0; Working && (i < Settings.TrackerItems.Length); i++ )
                    {
                        Log4cs.Log("Going to check [{0}]: {1}", i, Settings.TrackerItems[i].Name);
                        m_webClient.DownloadStringAsync(Settings.TrackerItems[i].SiteUri, Settings.TrackerItems[i]);
                        // Sleeps until timeout or Exit event is fired
                        eventId = Misc.WaitMyEvents(ref Settings.Events.ArEvents, arEventIds, Settings.TrackerItems[i].FrequencyS * 1000, true);

                        Log4cs.Log("Got event: {0}", eventId == ManualResetEvent.WaitTimeout ? "TIMEOUT" : Settings.Events.IdToName(eventId));
                    }

                    if( AllSitesDone != null )
                    {
                        Log4cs.Log(Importance.Debug, "All sites are checked, fire AllSitesDone event...");
                        AllSitesDone(Settings.TrackerItems);
                    }
                    //throw new Exception("Stopping by myself!");

                    // Sleep if thread is still working
                    if( Working )
                    {
                        // Sleeps until timeout or Exit event is fired
                        eventId = Misc.WaitMyEvents(ref Settings.Events.ArEvents, arEventIds, 30 * 60 * 1000, true);
                        
                    }
                }

            } catch( Exception ex )
            {
                Log4cs.Log(Importance.Error, "Error in tracker thread!");
                Log4cs.Log(Importance.Debug, ex.ToString());
            }

            Log4cs.Log("Tacker thread ends...");
            m_thread = null;
        }


        internal void Stop()
        {
            Log4cs.Log("Stopping tracker thread...");
            Working = false;
            Log4cs.Log("Firing Exit ({0}) event for threads to exit...", Settings.Events.EventId.Exit);
            Settings.Events.ArEvents[Settings.Events.EventId.Exit].Set();
        }
    }
}
